package com.meshnetics.gb.type.impl.zcl;

import com.meshnetics.gb.type.DataType;
import com.meshnetics.gb.type.DataTypeID;
import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.impl.GlobalFinalTypesRegistry;
import com.meshnetics.gb.type.impl.StringUtils;

/**
 * Abstract implementation of DataType. Holds aliases and data type id. <br> Provides equals(Object) based on checking
 * name, data type id and class (class must be the same). Hash code based on name and data type id. To string returning
 * simple name of class and name of type. <br> Abstract mandatory formatter is provided. Call of valueOf(String) is
 * redirected to formatter. <br> Intended for extending with deserialization/valueOf methods implemented
 * (valueOf(String) - in formatter should be implemented).
 *
 * @author AKovylin
 * @author Pavlov Dmitry
 */
public abstract class AbstractDataType implements DataType {
    private final String[] aliases;
    private final DataTypeID dataTypeId;

    protected AbstractDataType(String[] aliases) {
        this(aliases, null);
    }

    protected AbstractDataType(String[] aliases, DataTypeID dataTypeId) {
        if (aliases == null) {
            throw new NullPointerException("aliases is null");
        }
        if (aliases.length == 0) {
            throw new IllegalArgumentException("aliases must contain atleast one element");
        }
        this.aliases = aliases;
        this.dataTypeId = dataTypeId;
        GlobalFinalTypesRegistry.INSTANCE.registerType(this);
    }

    /**
     * Initializes the name of data type
     *
     * @param alias name of the type
     * @param register if this value {@code true} then type will be registered in types registry. If it is {@code false}
     * it won't be registered
     */
    protected AbstractDataType(String alias, boolean register) {
        if (alias == null) {
            throw new NullPointerException("alias is null");
        }

        aliases = new String[]{alias};
        this.dataTypeId = null;

        if (register) {
            GlobalFinalTypesRegistry.INSTANCE.registerType(this);
        }
    }

    public String getName() {
        return aliases[0];
    }

    public String[] getAliases() {
        return (String[]) aliases.clone();
    }

    public DataTypeID getDataTypeID() {
        return dataTypeId;
    }

    public DataValue valueOf(String value) {
        return getMandatoryFormatter().valueOf(value);
    }

    public boolean equals(Object obj) {
        return obj != null && getClass() == obj.getClass() &&
                this.aliases[0].equals(((AbstractDataType) obj).aliases[0]) &&
                (this.dataTypeId == null ? ((AbstractDataType) obj).dataTypeId == null :
                        this.dataTypeId.equals(((AbstractDataType) obj).dataTypeId));
    }

    public int hashCode() {
        return aliases[0].hashCode() + (dataTypeId == null ? 0 : 29 * dataTypeId.hashCode());
    }

    public String toString() {
        return StringUtils.getSimpleName(getClass()) + '(' + getName() + ')';
    }
}
